-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Cursor Trail Feature to Enhance Cursor Visibility #7970
Conversation
Fix the CI failures please. WIll review in detail when I have a moment. You can send some trivial PR that I will merge so that this PR does not need me to approve running CI for every change. |
Regarding the nvim issue, IIRC nvim uses synchronized updates. I havent looked at your code in detail to se eif it already does this, but it may be possible to work around it by only triggerring the trail based on cursor movements after the synchronized update is over (this is exposed in kitty as screen->paused_rendering |
Thanks for the insight. I wasn't aware of the details surrounding synchronized updates and terminal modes, so this has been a great learning opportunity for me! I tried monitoring the pause state and cursor visibility inside the update_cursor_trail function, like this:
Here’s what I observed:
It seems that Neovim doesn't trigger paused_rendering during searches, instead, it's controlling the visibility of the cursor. I considered using the DECTCEM state to decide whether to follow the cursor, as it seemed logically sensible, though it doesn't solve the issue. However, I encountered another problem: DECTCEM remains off while in Neovim's terminal mode. Since terminal mode is used not only for regular terminal usage but also by many plugins that implement floating windows inside Neovim, I can't rely on this state, as I want the trail to work in terminal mode as well.
|
I still believe this is a Neovim issue. I found that preventing the |
FYI, the issue does not occur in VIM 9.1 |
On Tue, Oct 15, 2024 at 08:15:01AM -0700, Jinhwan Choi wrote:
I still believe this is a Neovim issue. I found that preventing the `filetype.lua` plugin (one of the default plugins) from loading resolves the problem. It is slowing down searching ui to update somehow. It's strange because this is a filetype plugin, and I wouldn't expect filetypes to have anything to do with searching.
Report it to neovim then, you can get a trace of the commands neovim is
sending kitty with kitty --dump-commands that should be enough to
demonstrate the issue without your PR on released kitty. Look for
move_cursor, hide_cursor etc in the log. neovim is presumably redrawing
the search bar/status bar when filetype.lua is loaded and doing a
search, causing the cursor jump. Maybe they will be willing to fix that.
In general though, I think you should take synchronized updates into
account. That is cursor movement command received while paused_rendering
is enabled should not trigger trails. This is consistent with the
semantics of synchronized updates which is that nothing on screen should
change while rendering is paused for an update.
|
The commit I just pushed will do that. Can you suggest an app or a situation that triggers paused_rendering for an example? So that I can see it actually affecting the behavior of the trail.
I'll try that when I have a moment. Thanks for the insight again! |
On Tue, Oct 15, 2024 at 04:08:48PM -0700, Jinhwan Choi wrote:
> In general though, I think you should take synchronized updates into
account. That is cursor movement command received while paused_rendering
is enabled should not trigger trails. This is consistent with the
semantics of synchronized updates which is that nothing on screen should
change while rendering is paused for an update.
The commit I just pushed will do that. Can you suggest an app or a situation that triggers paused_rendering for an example? So that I can see it actually affecting the behavior or the trail.
The various kittens like diff, themes, choose_fonts all use it. I think
some of the more modern terminal text editors also use it but I am not
sure.
|
I have merged, so that your work can form the basis of further changes. I have changed the cursor_trail option to allow specifying the wait time instead of using input_delay. Some notes, please fix these in a new PR When cursor=none (i.e. the cursor uses a reverse video effect the Move the calculation of the trail color into shader.c and pass a As far as I can tell, the trail is drawn even when the cursor is hidden Regarding paused_rendering. Currently as far as I can see, when |
Thanks for merging and the great feedback! I'll dive into the points and address them in a new PR soon.
When cursor is hidden. The intended behavior of the trail is it still be visible until it reaches the last position where the cursor was visible, then vanishes. I think it feels more natural than it suddenly disappear in the middle of its transition.
Same as above, the idea is that when entering paused_rendering, the trail moves until it reaches the last known position of the cursor, stay there, and resume following once unpaused. So it shouldn't need to track cursor moves during the pause. Let me know if you want me to rethink these behaviors! |
On Fri, Oct 18, 2024 at 11:38:28PM -0700, Jinhwan Choi wrote:
Thanks for merging and the great feedback! I'll dive into these points and work on addressing them in a new PR soon.
```
As far as I can tell, the trail is drawn even when the cursor is hidden
by the application. Is this intentional?
```
When cursor is hidden. The intended behavior of the trail is it still be visible until it reaches the position where the cursor was visible last, then vanishes. I think it feels more natural than it suddenly disappear in the middle of its transition.
My concern is that trails should not start due to movements that happen
while the cursor is hidden. This is because many applications hide the
cursor then update their UI then reshow the cursor. This might fix your
nvim search bounce issue as well. It should be possible to implement
this by either not changing Cursor->position_changed_by_client_at when
the cursor is hidden or more correctly not starting a trail when the
cursor is hidden.
```
Regarding paused_rendering. Currently as far as I can see, when
rendering is paused the cursor trail is not updated. This is correct,
but also the last cursor move time that you stored on the cursor object
should possibly be updated when rendering is unpaused. Essentially the
semantic should be as if all commands that happen during a paused
rendering all happen at the same instant, when the rendering is
unpaused. You can probably implement this by setting a flag on the
paused rendering object indicating cursor position was updated and if
that flag is set at unpause update the time.
```
Same as above, the idea is that when entering paused_rendering, the trail moves until it reaches the known position of the cursor, stay there, and resume following once unpaused. So it shouldn't need to track cursor moves during the pause.
Similarly, I want to basically ensure no trail is started while in
rendering paused mode and that for cursor movements that happen in that
mode the position_changed_by_client_at field is the time at which
rendering was resumed. This is the "correct" value for this field and
ensures that any code conditional on the value of this field behaves as
expected. If you want a pre-existing trail to continue being rendered
upto the position of the cursor when rendering was paused, then that
should be handled separately.
|
kitty-trail.mp4awesome work! just a video of me playing around 😂
|
Oh man now I need to play snake. |
Thank you for creating the animation! It's really stunning, but in my view, there's a minor shortcoming that's quite bothersome. When the beam cursor moves over a very short distance, it can cause a trailing shadow, and even feel like it's vibrating, which is a bit dizzying to the eyes. For example, when I'm typing in insert mode in nvim and deleting text (this issue is particularly noticeable at such times). 2024-10-21.22-11-46.mov |
thank you so much. i will try it. |
Above it says that the new config option is |
Sorry for confusion. The option actually changed after we merged this PR.
I fixed the original PR description. |
aaaaawesome!!! Thank you for your contribution to the animation, the current is incredibly smooth! |
@jinhwanlazy did you file the issue for neovim? |
resizing the terminal seems to trigger the trail asdf.mp4 |
That is because the shell redraws the prompt on resize which involves cursor movement. |
@leiserfg Not yet. I tried |
For neovim I see an issue where if the cursor isn't on the first column and I navigate up/down - for some reason the updates are significantly slower kitty_cursor_s.mov |
Looks like the trail animation is blocking key input from what I see in the video, but I have no idea why that's happening. Can you give me more details about your setup so I can try to reproduce this? Machine, OS, desktop environment, and your full kitty config would be helpful. |
Seeing the same thing with zero config nvim and normal nvim, don't think it's related to any config/plugins - nvim --version
NVIM v0.11.0-dev-996+g395f420fc
Build type: Release
LuaJIT 2.1.1727870382
# nvim build command
git clone https://github.com/neovim/neovim.git && cd neovim
cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE=Release -D ENABLE_WASMTIME=ON
cmake --build .deps
cmake -B build -G Ninja -D CMAKE_BUILD_TYPE=Release -D ENABLE_WASMTIME=ON
cmake --build build
sudo cmake --install build
kitty --version
kitty 0.37.0 created by Kovid Goyal kitty.conf # vim:fileencoding=utf-8:foldmethod=marker
include kitty-theme.conf
listen_on unix:/tmp/mykitty
allow_remote_control yes
editor nvim
#: Performance tuning {{{
repaint_delay 0
input_delay 0
sync_to_monitor no
macos_colorspace displayp3
text_composition_strategy 1.2 20
#: }}}
# shell_integration enabled
shell_integration no-cursor
macos_quit_when_last_window_closed no
macos_show_window_title_in none
macos_option_as_alt yes
cursor_blink_interval 0
initial_window_width 170c
initial_window_height 64c
enable_audio_bell no
strip_trailing_spaces smart
open_url_with default
update_check_interval 24
env LANG=en_US.UTF-8
env XDG_DATA_DIRS=/usr/local/share:/usr/share:/Users/kuro/.nix-profile/share:/nix/var/nix/profiles/default/share
env EDITOR=nvim
env VISUAL=nvim
env LC_CTYPE=UTF-8
confirm_os_window_close 1
map alt+r load_config_file
map alt+h toggle_layout stack
map opt+cmd+, debug_config
mouse_map left click ungrabbed mouse_handle_click prompt
mouse_map ctrl+left click ungrabbed mouse_handle_click link
map cmd+shift+t launch --cwd=current
map cmd+shift+enter launch --cwd=current
map cmd+shift+w close_window
map cmd+shift+k launch --cwd=current --type=os-window
map cmd+] next_window
map cmd+shift+. move_tab_forward
map cmd+shift+, move_tab_backward
map ctrl+f toggle_fullscreen
map cmd+shift+equal change_font_size current +1
map cmd+shift+minus change_font_size current -1
map cmd+shift+0 change_font_size current 0
map ctrl+k scroll_line_up
map shift+ctrl+k scroll_page_up
map ctrl+j scroll_line_down
map shift+ctrl+j scroll_page_down
map cmd+s send_text all \x1b:write\ \x0d
map cmd+shift+c copy_to_clipboard
map cmd+c send_text all \x1b[57376u
map cmd+1 next_window
map cmd+p show_first_command_output_on_screen
mouse_hide_wait -1.0
font_size 18.0
draw_minimal_borders yes
# inactive_text_alpha 0.8
tab_bar_edge bottom
window_border_width 1px
window_margin_width 0
window_padding_width 7
active_tab_background #0f4b6e
inactive_tab_background #1e1f2e
active_tab_foreground #e5e7eb
inactive_tab_foreground #7f8694
tab_bar_edge bottom
tab_bar_align left
tab_bar_style custom
tab_bar_min_tabs 1
tab_activity_symbol none
bell_on_tab no
tab_separator ""
tab_bar_margin_width 0.0
tab_bar_margin_height 0.0 0.0
tab_title_template "{f'{title[:30]}…' if title.rindex(title[-1]) + 1 > 30 else (title.center(6) if (title.rindex(title[-1]) + 1) % 2 == 0 else title.center(5))}"
active_tab_font_style italic
active_border_color none
inactive_border_color #717171
color0 #1E1E2E
color15 #FFFFFF
color16 #FFFF00
# https://github.com/ryanoasis/nerd-fonts/wiki/Glyph-Sets-and-Code-Points
# Seti-UI + Custom
symbol_map U+e5fa-U+e6b5 Symbols Nerd Font
# Devicons
symbol_map U+e700-U+e7c5 Symbols Nerd Font
# Font Awesome
symbol_map U+ed00-U+f2ff Symbols Nerd Font
# Font Awesome Extension
symbol_map U+e200-U+e2a9 Symbols Nerd Font
# Material Design Icons
symbol_map U+f0001-U+f1af0 Symbols Nerd Font
# Weather
symbol_map U+e300-U+e3e3 Symbols Nerd Font
# Octicons
symbol_map U+f400-U+f533,U+2665,U+26A1 Symbols Nerd Font
# [Powerline Symbols]
symbol_map U+e0a0-U+e0a2,U+e0b0-U+e0b3 Symbols Nerd Font
# Powerline Extra Symbols
symbol_map U+e0a3,U+e0b4-U+e0c8,U+e0ca,U+e0cc-U+e0d7 Symbols Nerd Font
# IEC Power Symbols
symbol_map U+23fb-U+23fe,U+2b58 Symbols Nerd Font
# Font Logos (Formerly Font Linux)
symbol_map U+f300-U+f375 Symbols Nerd Font
# Pomicons
symbol_map U+e000-U+e00a Symbols Nerd Font
# Codicons
symbol_map U+ea60-U+ec1e Symbols Nerd Font
# Additional sets
symbol_map U+276c-U+2771,U+2500-U+259f Symbols Nerd Font
#: }}}
#
# symbol_map U+f300-U+f313,U+e000-U+e00a,U+ea60-U+ebeb,U+e0a0-U+e0c8,U+e0ca,U+e0cc-U+e0d4,U+e200-U+e2a9,U+e300-U+e3e3,U+e5fa-U+e6b1,U+e700-U+e7c5,U+f000-U+f2e0,U+f300-U+f372,U+f400-U+f532,U+f0001-U+f1af0 Symbols Nerd Font Mono
#
#font_family family='SF Pro'
font_family family='JetBrains Mono' postscript_name=JetBrainsMono-Regular
bold_font auto
italic_font auto
bold_italic_font auto
font_features JetBrainsMono-Regular +calt +zero +cv07
font_features JetBrainsMono-Italic +calt +zero
cursor_trail 3
cursor_trail_decay 0.2 0.3
cursor_trail_start_threshold 3 arm64 m3 max System Version: macOS 15.2 (24C5073e)
Kernel Version: Darwin 24.2.0
Boot Volume: Macintosh HD
Boot Mode: Normal
Secure Virtual Memory: Enabled
System Integrity Protection: Enabled
Time since boot: 41 minutes, 33 seconds |
@kuro337
Simply setting |
@jinhwanlazy thanks so much for this! Is there any chance of adding an option to hide the cursor during the animation? ie.
That way the cursor would animate into position rather than instantly move but have a trail. Since kitty supports hiding the cursor for the blink animation I assume this is possible and fairly trivial to implement? |
@TRPB Thanks for the suggestion! I understand that you're looking for a seamless experience similar to what Neovide provides, where the cursor itself appears to morph into position. That’s the essence of Neovide’s "animated cursor," which differs from the "cursor trail" implementation in Kitty. Unfortunately, it’s not as straightforward as it might seem. Here’s what happens when we simply toggle cursor visibility: cursor_trail_demo_hide_cursor_while_animation.mov(Ignore the blinking artifact.) The real issue is that the trail hides text underneath it, regardless of cursor visibility. This happens because the cursor trail is essentially rendered as a plain opaque quad. I had to use a trick like this to ensure the glyph remains visible on the cursor while the trail is moving: kitty/kitty/trail_fragment.glsl Lines 10 to 12 in a5f70fb
It skips rendering on the cursor’s position. It would look even more awkward with this if the cursor is turned off. I’ve spent some time thinking about how to achieve Neovide’s animated cursor look and feel. @kovidgoyal and @mxple had a great discussion about it even before I started working on this feature. See: #2460 (comment). It helped alot for me to start. Here are the main options I’ve considered:
I looked into the cell shader and realized I might be able to reuse the sprite texture in the trail shader to pass through foreground colors. I’ll try implementing this idea soon. |
Thanks for looking into that so quickly! That's definitely a problem and not ideal, you're right, it's not as simple as I'd hoped it might be. Good effort so far! Looking at neovide (just using it, not the code) it appears that the cursor trail is always below the text. Could you draw the cursor trail on a layer between the background and the text? I don't know how the terminal differentiates the parts but kitty knows to apply transparency to the background. Whether it's even feasible to put another drawn layer in-between the background and the text I have no idea. Sorry for multiple requests but this is such an awesome feature. One other thing that would be nice is antialiasing on the trail. The lack of it is particularly apparent in your slowed down video. I would hope that it would be just enabling it as part of the shader but what do I know, I suggested hiding/showing the cursor should be "fairly trivial" |
OK, that could be option 4. There are 3 main rendering methods implemented in kitty: one that draws every element (background, cursor, text, image, etc.) in a single draw call, which is supposed to be concise and fast, and the other 2 that draws them one by one, each is triggered when transparency and a background image is involved. I might be able to make kitty to use the latter methods and insert a draw call for cursor_trail between the background and text, specifically here: Lines 898 to 899 in 8e388ac
The downside is this would mess with existing rendering logic, which I'm trying to avoid (Same reason as I'm unhappy with option 1). Resulting code would look messy unless some careful refactoring is done. e.g. the function should be renamed from |
draw_cells_interleaved just means the cells are drawn in multiple passes |
Looking again I don't think it will actually be quite that simple either, unfortunately. Because the character under the cursor is a different colour to the character before the cursor is over it. You original shader overlay might be better For example, a white cursor with a white character under it like your video, the semicolon is a different colour before and after the cursor is on it |
@kovidgoyal regarding your recent commit 4b12bff
These videos below are with and without your change. simplescreenrecorder-2024-12-12_10.08.16.mp4simplescreenrecorder-2024-12-12_10.11.09.mp4 |
If you want to restore it then implement it without using if and discard. Instead use step() to set a multiplier that will be zero outside the region and 1 inside and multiple the opacity by that. |
And I will note that I cant see any difference in your videos. In both cases during the large horizontal movement text is obscured to the same extent. I tested it with so that it can be easily followed by eye. And the result are identical with and without the patch. |
Ah never mind I think I got it, you mean you dont want the cursor trail drawn over the actual cursor position. |
This should take care of it: |
This pull request introduces a new feature that adds a trailing effect behind the cursor as it moves within the terminal window. The cursor trail enhances cursor visibility, making it easier to track cursor movement, especially during rapid navigation or in complex outputs. This feature is particularly beneficial when demonstrating tasks in the terminal, as it allows viewers to easily follow the cursor's movements.
related issue: #2460
video - cursor trail moves smoothly between kitty windows, tmux panes and vim splits.
cursor_trail_demo.mp4
Key Changes
New Configuration Option:enable_cursor_trail
Type: Boolean (yes
orno
)Default:no
Description: Enables or disables the cursor trail effect. When set toyes
, a trailing effect is rendered behind the cursor as it moves, creating a motion trail.New Configuration Option:
cursor_trail
New Configuration Option:
cursor_trail_decay
0.1 0.3
enable_cursor_trail
is set toyes
. This option accepts two positive float values specifying the fastest and slowest decay times in seconds. The first value corresponds to the fastest decay time (minimum), and the second value corresponds to the slowest decay time (maximum). The second value must be equal to or greater than the first value. Smaller values result in a faster decay of the cursor trail. Adjust these values to control how quickly the cursor trail fades away.Implementation Details
Cursor Trail Concept
CursorTrail Data Structure
CursorTrail
instance resides within theTab
structure, while the cursor itself is part of theScreen
. This design choice allows the trail to represent cursor movements across different windows within a tab.Rendering Logic
cursor_trail.c
to handle the updating and rendering of the cursor trail.child-monitor.c
andshader.c
to integrate the cursor trail update and rendering into the existing rendering pipeline.Trail Updating Rule
input_delay
, which means it ignores fast-moving cursors. This prevents trails from appearing for cursors that rapidly change positions, such as those used for drawing UI elements in certain applications.n
commands, the trail may jump between the search bar and the search result. This appears to be an issue with Neovim's cursor handling, as running Neovim withnvim -u NONE
does not exhibit this behavior.video - bug in nvim(?)
cursor_trail_demo_vimsearch.mp4